/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator;

/**
 *
 * @author Laurent WOUTERS
 */
public abstract class PropertyRepresentation {
    protected ClassGenerator parentClass;
    protected PropertyGenerator property;
    protected ClassGenerator rangeClass;
    protected JavaDatatype rangeDatatype;
    protected int cardMin;
    protected int cardMax;
    protected boolean hasSelf;
    protected java.util.List<InstanceGenerator> hasObjectValue;
    protected java.util.List<xowl.lang.runtime.Literal> hasDataValue;
    protected boolean restrictType;
    protected boolean inTypeRestrictionChain;

    public ClassGenerator getParentClass() { return parentClass; }
    public PropertyGenerator getProperty() { return property; }
    public ClassGenerator getRangeClass() { return rangeClass; }
    public JavaDatatype getRangeDatatype() { return rangeDatatype; }
    public int getCardMin() { return cardMin; }
    public int getCardMax() { return cardMax; }
    public boolean hasSelf() { return hasSelf; }
    public boolean isVector() { return (cardMax > 1); }
    public String getRepresentationRange() {
        if (property.isObjectProperty())
            return rangeClass.getJavaName();
        else
            return rangeDatatype.getJavaType();
    }
    public boolean hasValues() { return !(hasObjectValue.isEmpty() && hasDataValue.isEmpty()); }
    public int getValuesCount() { return hasObjectValue.size() + hasDataValue.size(); }
    public java.util.List<InstanceGenerator> hasObjectValues() { return hasObjectValue; }
    public java.util.List<xowl.lang.runtime.Literal> hasDataValues() { return hasDataValue; }
    public boolean restrictType() { return restrictType; }

    public PropertyRepresentation(ClassGenerator classe, PropertyGenerator property) {
        this.parentClass = classe;
        this.property = property;
        this.rangeClass = property.getRangeClass();
        this.rangeDatatype = property.getRangeDatatype();
        this.cardMin = 0;
        this.cardMax = Integer.MAX_VALUE;
        this.hasSelf = false;
        this.hasObjectValue = new java.util.ArrayList<InstanceGenerator>();
        this.hasDataValue = new java.util.ArrayList<xowl.lang.runtime.Literal>();
        this.restrictType = false;
        this.inTypeRestrictionChain = false;
        if (this.property.isFunctional())
            cardMax = 1;
        applyRestrictions();
        if (this.property.isObjectProperty()) {
            if (rangeClass == null)
                rangeClass = parentClass.getClassGenerator(this.parentClass.getPackage().getModel().getRepository().interpretAsClass(parentClass.getPackage().getModel().getRepository().resolveEntity(ModelGenerator.owlThing)));
        } else {
            if (rangeDatatype == null)
                rangeDatatype = property.getJavaDatatype(parentClass.getPackage().getModel().getRepository().interpretAsDatatype(parentClass.getPackage().getModel().getRepository().resolveEntity(JavaDatatype.xsdString)));
        }
    }

    private void applyRestrictions() {
        for (ClassGenerator gen : parentClass.getEquivalencyGroup()) {
            for (xowl.lang.runtime.ClassRestriction restriction : gen.getOWLClass().getAllClassRestrictions())
                applyRestriction(restriction);
            for (ClassGenerator sup : gen.getSuperClasses())
                for (xowl.lang.runtime.ClassRestriction r : sup.getOWLClass().getAllClassRestrictions())
                    applyRestriction(r);
        }
    }
    private void applyRestriction(xowl.lang.runtime.ClassRestriction restriction) {
        if (restriction instanceof xowl.lang.runtime.ObjectPropertyRestriction)
            applyRestriction_ObjectProperty((xowl.lang.runtime.ObjectPropertyRestriction)restriction);
        if (restriction instanceof xowl.lang.runtime.DataPropertyRestriction)
            applyRestriction_DataProperty((xowl.lang.runtime.DataPropertyRestriction)restriction);
        if (restriction instanceof xowl.lang.runtime.NAryDataPropertyRestriction)
            applyRestriction_NAryProperties((xowl.lang.runtime.NAryDataPropertyRestriction)restriction);
    }
    private void applyRestriction_ObjectProperty(xowl.lang.runtime.ObjectPropertyRestriction restriction) {
        if (property.getOWLProperty() != restriction.getObjectProperty())
            return;
        if (restriction instanceof xowl.lang.runtime.ObjectAllValuesFrom) {
            xowl.lang.runtime.ObjectAllValuesFrom r = (xowl.lang.runtime.ObjectAllValuesFrom)restriction;
            ClassGenerator classe = parentClass.getClassGenerator((xowl.lang.runtime.Class)r.getClasse());
            if ((rangeClass == null) || rangeClass.getSubClasses().contains(classe)) {
                rangeClass = classe;
                restrictType = true;
            }
        } else if (restriction instanceof xowl.lang.runtime.ObjectSomeValuesFrom) {

        } else if (restriction instanceof xowl.lang.runtime.ObjectHasSelf) {
            hasSelf = true;
        } else if (restriction instanceof xowl.lang.runtime.ObjectHasValue) {
            xowl.lang.runtime.ObjectHasValue r = (xowl.lang.runtime.ObjectHasValue)restriction;
            xowl.lang.runtime.Individual ind = (xowl.lang.runtime.Individual)r.getIndividual();
            if (ind instanceof xowl.lang.runtime.NamedIndividual) {
                xowl.lang.runtime.Class c = ind.getAllClassifiedBy().iterator().next();
                ClassGenerator cgen = parentClass.getClassGenerator(c);
                InstanceGenerator inst = cgen.getStaticInstance((xowl.lang.runtime.NamedIndividual)ind);
                hasObjectValue.add(inst);
            }
        } else if (restriction instanceof xowl.lang.runtime.ObjectExactCardinality) {
            xowl.lang.runtime.ObjectExactCardinality r = (xowl.lang.runtime.ObjectExactCardinality)restriction;
            int value = r.getCardinality();
            cardMin = value;
            cardMax = value;
        } else if (restriction instanceof xowl.lang.runtime.ObjectMinCardinality) {
            xowl.lang.runtime.ObjectMinCardinality r = (xowl.lang.runtime.ObjectMinCardinality)restriction;
            int value = r.getCardinality();
            if (cardMin < value)
                cardMin = value;
        } else if (restriction instanceof xowl.lang.runtime.ObjectMaxCardinality) {
            xowl.lang.runtime.ObjectMaxCardinality r = (xowl.lang.runtime.ObjectMaxCardinality)restriction;
            int value = r.getCardinality();
            if (cardMax > value)
                cardMax = value;
        }
    }
    private void applyRestriction_DataProperty(xowl.lang.runtime.DataPropertyRestriction restriction) {
        if (property.getOWLProperty() != restriction.getDataProperty())
            return;
        if (restriction instanceof xowl.lang.runtime.DataHasValue) {
            xowl.lang.runtime.DataHasValue r = (xowl.lang.runtime.DataHasValue)restriction;
            xowl.lang.runtime.Literal lit = (xowl.lang.runtime.Literal)r.getLiteral();
            hasDataValue.add(lit);
        } else if (restriction instanceof xowl.lang.runtime.DataExactCardinality) {
            xowl.lang.runtime.DataExactCardinality r = (xowl.lang.runtime.DataExactCardinality)restriction;
            int value = r.getCardinality();
            cardMin = value;
            cardMax = value;
        } else if (restriction instanceof xowl.lang.runtime.DataMinCardinality) {
            xowl.lang.runtime.DataMinCardinality r = (xowl.lang.runtime.DataMinCardinality)restriction;
            int value = r.getCardinality();
            if (cardMin < value)
                cardMin = value;
        } else if (restriction instanceof xowl.lang.runtime.DataMaxCardinality) {
            xowl.lang.runtime.DataMaxCardinality r = (xowl.lang.runtime.DataMaxCardinality)restriction;
            int value = r.getCardinality();
            if (cardMax > value)
                cardMax = value;
        }
    }
    private void applyRestriction_NAryProperties(xowl.lang.runtime.NAryDataPropertyRestriction restriction) {
        if (!restriction.getAllDataProperties().contains((xowl.lang.runtime.DataProperty)property.getOWLProperty()))
            return;
        if (restriction instanceof xowl.lang.runtime.DataAllValuesFrom) {
            xowl.lang.runtime.Datatype datatype = restriction.getDatatype();
            rangeDatatype = property.getJavaDatatype(datatype);
            restrictType = true;
        } else if (restriction instanceof xowl.lang.runtime.DataSomeValuesFrom) {

        }
    }


    public static xowl.lang.runtime.Property getRestrictedProperty(xowl.lang.runtime.ClassRestriction restriction) {
        if (restriction instanceof xowl.lang.runtime.ObjectPropertyRestriction)
            return (xowl.lang.runtime.Property)((xowl.lang.runtime.ObjectPropertyRestriction)restriction).getObjectProperty();
        if (restriction instanceof xowl.lang.runtime.DataPropertyRestriction)
            return (xowl.lang.runtime.Property)((xowl.lang.runtime.DataPropertyRestriction)restriction).getDataProperty();
        return ((xowl.lang.runtime.NAryDataPropertyRestriction)restriction).getAllDataProperties().iterator().next();
    }
}
